网易云课堂Excel课程爬虫思路
由于即将毕业,马上进入职场,想来是时候需要巩固一下基本职场技能了,特别是Excel这种杀手级职场应用。
可是如今网络这么发达,到处都充斥着Excel课程、视频、教程,真的很容易让人眼花缭乱,不知所措。
看书来的太慢了,还是直接看视频吧,简单粗暴,学习之前总要熟悉一下Excel教学行业的大致情况吧,今天就拿网易云课堂的Excel板块作为目标,在练习数据爬取的同时,顺便了解一下Excel培训行业的行情,知己知彼才能百战不殆,才能更加集中精力的学习那些精品课程。
url<-"http://study.163.com/category/excel#/"
#Excel板块首页网址
url<-"http://study.163.com/category/excel#/?p=2"
#加载第二页之后的网址
网易云课堂的网页不是很复杂,而且URL是很规律的参数拼接,反倒最底部可以看到,它是点击翻页,一共只有9页,而且页面是顺序加载,OK,可以直接手动拼接遍历网址了。
url<-paste0("http://study.163.com/category/excel#/?p=",1:9)
library("rvest")
library("XML")
library("RCurl")
postForm(url[1],)
web<-read_html(url[1],encoding="UTF-8")%>%
html_nodes("div.uc-ykt-coursecard-wrap_tit > h3") %>% html_text()
but以上尝试都失败了!
当我想当然的以为网易云课堂用R可以轻松搞定的时候,猛然发现他用的XHR技术,奔溃……
首先我们再次分析网页,打开云课堂Excel模块首页,按F12翻到XHR菜单
在底部你可以看到studycourse.json文件,点开之后可以看到右侧的Preview栏目里面全部都是课程信息,是一个json文件。
这个模块是Chrome的开发者工具后台,就是我们常说的抓包工具,现在切换到Headers栏目,可以看到云课堂所有的课程信息都是在一个.josn网页里面存放着,这里便是阻碍我们使用普通方法爬取数据的困难之源。
仔细看你会发现General里面用到的Request Method 是Post,Post方法在 提交网址和参数的同时,要提交表单数据,这时候我们需要详细的查看Request Headers里面的参数信息。
因为POST方法涉及到传递表单参数,所以构造报头一定要添加Content-Type参数,这里的Content-Type参数是application/json,需要传递json字符串。
看来今天这个案例用R语言有些困哪了(使用 webdriver除了偷懒,并不能锻炼你什么能力),本案例POST要传递json表单参数,R里面没有很多的处理json的方式,再加上RCurl里面的POST方法资料太少,没有什么可参考资料。(还是R语言的爬虫生态太弱了)。
所以今天用Pyhton来演示本案例:
import json
import requests
import pandas as pd
import os
第一步:分析XHR中POST方法的表单规律:
使用POST方法爬取数据,需要重点关注Request Headers中的User-Agent、Content-Type以及最后面的表单体。
本例的网页,虽然看上去分了9个子页面,但是实际上其调用的json仅有一个主页:http://study.163.com/p/search/studycourse.json
而不同子页面主要是用过表单体中的参数来确定到的。
{"pageIndex":1,"pageSize":50,"relativeOffset":0,"frontCategoryId":"400000000149040","searchTimeType":-1,"orderType":0,"priceType":-1,"activityId":0}
{"pageIndex":2,"pageSize":50,"relativeOffset":50,"frontCategoryId":"400000000149040","searchTimeType":-1,"orderType":0,"priceType":-1,"activityId":0}
{"pageIndex":3,"pageSize":50,"relativeOffset":100,"frontCategoryId":"400000000149040","searchTimeType":-1,"orderType":0,"priceType":-1,"activityId":0}
……
{"pageIndex":9,"pageSize":50,"relativeOffset":400,"frontCategoryId":"400000000149040","searchTimeType":-1,"orderType":0,"priceType":-1,"activityId":0}
以上我给出了9个页面的表单体信息中的前三个和最后一个,通过找规律你会发现,差异仅在pageIndex和relativeOffset参数上,其他参数都是一样的。pageIndex和relativeOffset分别代表页面id和主页中信息条目的偏移量。偏移量间隔50,也就是我们在网页上看到的单页展示课程数目。
构造表单体:
payload = {
"pageIndex":1,
"pageSize":50,
"relativeOffset":100,
"frontCategoryId":"400000000149040"
}
一共9个子页面,通过表单体参数进行页面遍历控制
构造报头信息:
headers = {
'Accept':'application/json',
'content-type':'application/json',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36'
}
第二步:构造网页爬取函数:
url = 'http://study.163.com/p/search/studycourse.json'
r=requests.post(url,data=json.dumps(payload),headers=headers);r
<Response [200]>
状态码OK!
content=r.json();content
type(content)
dict ###返回内容类型为字典
返回的是一个字典,里面嵌套有很多层,仔细观察你会先发,我们需要的内容都存放在content['result']['list']里面
content['result']['list']
type(content['result']['list'])
list ###返回类型是列表
OK,构造一个循环,将每一次请求返回提取的内容拼接在一个列表里面:
fullinfo=[]
for i in list(range(1,10)):
payload['pageIndex']=i
payload['relativeOffset']=50*i-50
r=requests.post(url,data=json.dumps(payload),headers=headers)
content=r.json()
fullinfo=fullinfo+content['result']['list']
print("第{}部分已加载".format(i))
df1=pd.DataFrame(fullinfo)
df1.columns ###返回所有列信息
Index(['activityIds', 'bigImgUrl', 'courseCardProps', 'courseId',
'description', 'discountPrice', 'discountRate', 'displayType',
'endTime', 'forumTagLector', 'gmtModified', 'imgUrl', 'isPromStatus',
'learnerCount', 'lectorName', 'originalPrice', 'productId',
'productName', 'productType', 'provider', 'published',
'schoolShortName', 'score', 'scoreLevel', 'startTime', 'tagIap',
'tagLectorTime'],
dtype='object')
###提取我们需要的列:
mydata=df1[["productName","discountPrice","discountRate","lectorName","originalPrice","description","provider","score","scoreLevel"]]
存储本地:
os.chdir('D:/Python/Data')
mydata.to_csv('yunketang.csv',index=False)
存储到本地硬盘,搞完收工!一共421条Excel课程信息,和后台的信息一致。
下一篇针对这一次爬虫结果做可视化分析!
在线课程请点击文末阅读原文: